package Concurrency2.Chapter06_AQS;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * Instruction：通过CLH思想实现一个CLH锁, 该思想的目的: 减少CAS造成的总线风暴
 * 具体逻辑:
 * 通过实现一个单向链表, 让争取锁的队列排队, 有先来后到;
 * 先来一个Thread抢占锁, 进入队列tail位置排队
 * 队头的Thread占有锁
 * 所有的队员, 都持续关注其前驱节点, 如果锁释放了, 那么就去获取锁.
 * Author：@author MaLi
 */
public class T01_CLHLock implements Lock {

    //记录当前Node
    private ThreadLocal<CLHNode> currentNode = new ThreadLocal<>();
    //用于修改队尾
    private AtomicReference<CLHNode> tail = new AtomicReference<>(null);

    // 初始化锁
    public T01_CLHLock() {
        tail.getAndSet(CLHNode.emptyNode);
    }

    @Override
    public void lock() {
        //有线程来申请锁
        //创建一个新的Node放到tail位置
        CLHNode preNode = tail.get();//当前的Node
        CLHNode curNode = new CLHNode(null, true);
        while (!tail.compareAndSet(preNode, curNode)) {
            preNode = tail.get();// 如果没有获取到最新的Node, 就再次获取, 直到将curNode成功设置为Tail
        }
        //开始关注前驱节点, 是否释放锁
        while (curNode.getPrevNode().isLocked()) {
            Thread.yield();
        }
        //如果能执行到这里, 说明成功的获取到了锁
        currentNode.set(curNode);

    }

    @Override
    public void unlock() {
        CLHNode curNode = currentNode.get();
        curNode.setLocked(false);//释放锁
        curNode.setPrevNode(null);//GC掉前驱节点
        currentNode.set(null);
    }

    /**
     * 代表CLH队列元素
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    private static class CLHNode {
        private CLHNode prevNode;  //其前驱节点
        private volatile boolean isLocked; //标识是否占有或者是否在获取锁
        private static CLHNode emptyNode = new CLHNode(null,false);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock() {
        return false;
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }


    @Override
    public Condition newCondition() {
        return null;
    }
}
